package network

import (
	"embed"
	"fmt"
	"log"
	"net"
	"sync"

	. "github.com/ahmetb/go-linq/v3"
)

//go:embed dist/favicon.ico
var EmbedFs embed.FS
var Port uint16

type ServerInfo struct {
	interfaces []*net.UDPConn
	servers    []net.Conn
	lock       sync.Mutex
}

var Server_list ServerInfo

func Discovery() {
	var err error
	udp_addr, err := net.ResolveUDPAddr("udp", "239.0.2.183:9999")
	if err != nil {
		log.Fatal(err)
	}
	ifaces, err := net.Interfaces()
	if err != nil {
		log.Print("get interface error")
		return
	}
	Server_list = ServerInfo{servers: make([]net.Conn, 0)}
	for _, iface := range ifaces {
		if err != nil {
			log.Print("get address error")
			continue
		}
		if iface.Flags&net.FlagUp != net.FlagUp ||
			iface.Flags&net.FlagMulticast != net.FlagMulticast ||
			iface.Flags&net.FlagLoopback == net.FlagLoopback {
			continue
		}
		conn, err := net.ListenMulticastUDP("udp", &iface, udp_addr)
		if err != nil {
			log.Print(err)
			continue
		}

		Server_list.lock.Lock()
		Server_list.interfaces = append(Server_list.interfaces, conn)
		Server_list.lock.Unlock()
		go func() {
			buf := make([]byte, 5)
			for {
				_, clientAddr, err := conn.ReadFromUDP(buf)
				if err == nil {
					if string(buf) == "hello" {
						con, err := net.Dial("tcp", clientAddr.IP.String()+":"+fmt.Sprint(Port))
						if err == nil {
							Server_list.lock.Lock()
							Server_list.servers = append(Server_list.servers, con)
							Server_list.lock.Unlock()
							go ReceiveData(con)
							log.Print("add ", clientAddr.IP.String())
						}
					}
				}
			}
		}()

	}
}
func SendBroadCastMessage() {
	udp_addr, _ := net.ResolveUDPAddr("udp", "239.0.2.183:9999")
	Server_list.lock.Lock()
	From(Server_list.interfaces).ForEach(func(i interface{}) {
		i.(*net.UDPConn).WriteToUDP([]byte("hello"), udp_addr)
	})
	Server_list.lock.Unlock()
}
func DeleteConn(target net.Conn) {
	Server_list.lock.Lock()
	From(Server_list.servers).SkipWhile(func(i interface{}) bool {
		return i == target
	}).ToSlice(&Server_list.servers)
	Server_list.lock.Unlock()
	log.Print("delete ", target.RemoteAddr().String())
}
func GetServers() []string {
	server_list := make([]string, 0)
	Server_list.lock.Lock()
	From(Server_list.servers).Select(func(i interface{}) interface{} {
		return i.(net.Conn).RemoteAddr().String()
	}).ToSlice(&server_list)
	Server_list.lock.Unlock()
	return server_list
}
